#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libxml/xmlreader.h>

#include <xmlsec/buffer.h>
#include <xmlsec/xmlsec.h>
#include <xmlsec/crypto.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/list.h>
#include <xmlsec/xmldsig.h>

#include "common.h"

void yankNSrec(GHashTable *ht, xmlNodePtr cur) {
  xmlNsPtr nsp;
  xmlNodePtr sub;

  nsp = cur->nsDef;
  sub = cur->xmlChildrenNode;
  if(nsp)
    do {
      if(!nsp->prefix)
	continue;
      char *old = g_hash_table_lookup(ht, nsp->prefix);
      if(old && strcmp(old, nsp->href))
	fprintf(stderr, "Warning, prefix %s maps to %s and %s in doc!\n",
		nsp->prefix, old, nsp->href);
      else
	g_hash_table_insert(ht, (gpointer)nsp->prefix, (gpointer)nsp->href);
    } while((nsp = nsp->next));
  if(sub)
    do
      yankNSrec(ht, sub);
    while((sub = sub->next));
}

void addNS(gpointer prefix, gpointer href, gpointer node) {
  xmlNewNs(node, href, prefix);
/*   if(nsp) */
/*     xmlSetNs(node, nsp); */
//  fprintf(stderr, "adding %s:%s to root\n", (char *)href, (char *)prefix);
}

void yankNameSpaces(xmlNodePtr top) {
#if 0
  GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
  yankNSrec(ht, top);
  g_hash_table_foreach(ht, addNS, top);
  g_hash_table_destroy(ht);
#else
  return;
#endif
}

xmlSecDSigCtxPtr processSolicitation(xmlNodePtr root) {
  xmlNodePtr cur, signode;
  struct signedDocument *sdoc;
  struct trustState *ts;
  GSList *configs;
  xmlDocPtr prop, sigdoc;
  xmlNsPtr ns, xmlns;
  xmlSecDSigCtxPtr ds;

  fprintf(stderr, "starting processSol\n");
  ts = newTrustState();
  fprintf(stderr, "starting validate\n");
  sdoc = validateDocument(DATADIR "/charon.xsd", root);

  fprintf(stderr, "validated document\n");
  resolveCertificates(sdoc, ts);
  fprintf(stderr, "resolved certs\n");
  configs = configsForKey(ts, sdoc->signingKey);
  fprintf(stderr, "config for key\n");
  while(configs) {
    struct config *cfg = configs->data;
    GSList *tmp;
    
    cur = sdoc->signedRoot->xmlChildrenNode;
    if(cur)
      do {
	if(!xmlStrcmp(xmlSecGetNodeNsHref(cur), "urn:frog")) {
	  const char * curname = xmlSecNodeGetName(cur);
	  if(!xmlStrcmp(curname, "Address")) {
	    xmlChar *destination = xmlNodeListGetString(cur->doc, cur->xmlChildrenNode, 1);
	    xmlSecKeyPtr dns;
	    int ret;
	  
	    fprintf(stderr, "Address %s\n", destination);
	    if(!(ret = (int)g_hash_table_lookup(cfg->map, curname))) {
	      fprintf(stderr, "No config for %s, denying\n", curname);
	      goto nextconfig;
	    }
	    if(ret == 2) {
	      fprintf(stderr, "Permit for %s, continuing\n", curname);
	      continue;
	    }
	    fprintf(stderr, "Verify for %s...\n", curname);

	    // validate access to address
	    if((dns = getDNSKey("166.24.168.192.in-addr.arpa"))) {
	      fprintf(stderr, "Got key %p from DNS for %s\n", dns, destination);
	      fprintf(stderr, "findPath %d\n", findPath(ts, dns, sdoc->signingKey));
	    } else {
	      fprintf(stderr, "No key from DNS for %s\n", destination);
	      goto nextconfig;
	    }
	  } else if(!xmlStrcmp(curname, "External")) {
	    int cfgparm = (int)g_hash_table_lookup(cfg->map, curname);
	    if(cfgparm == 2) {
	      fprintf(stderr, "Permit for %s, continuing\n", curname);
	      continue;
	    }
	    // verify meaningless for External
	    goto nextconfig;
	  } else {
	    fprintf(stderr, "Unknown access %s\n", xmlSecNodeGetName(cur));
	    goto nextconfig;
	  }
	} else {
	  fprintf(stderr, "Skipping %s/%s\n", xmlSecNodeGetName(cur),
		 xmlSecGetNodeNsHref(cur));
	  goto nextconfig;
	}
      } while((cur = cur->next));
    else
      assert(!"No access requests!");

    prop = xmlNewDoc("1.0");
    cur = xmlNewDocNode(prop, NULL, "proposition", NULL);
    ns = xmlNewNs(cur, "urn:frog", "frog");
    xmlSetNs(cur, ns);
    xmlns = xmlNewNs(cur, "http://www.w3.org/XML/1998/namespace", "xml");
    xmlDocSetRootElement(prop, cur);
    xmlAddChild(cur, xmlDocCopyNode(sdoc->unsignedRoot, prop, 1));
    if(cfg->condition) {
      xmlNodePtr p, c;

      c = xmlDocCopyNode(cfg->condition, prop, 1);
      if((p = xmlSecFindNode(c, "ipsec", ns_mikolaj))) {
	char txt[100];
	
	snprintf(txt, 79, "%d", chooseSpi());
	xmlAddChild(p, xmlNewDocNode(p->doc, p->ns, "spi", txt));
      }
      xmlAddChild(cur, c);
    }
    sigdoc = xmlParseFile(DATADIR "/signature-proposition.xml");
    assert(sigdoc);
    signode = xmlDocCopyNode(xmlDocGetRootElement(sigdoc), prop, 1);
    assert(signode);
    xmlAddChild(cur, signode);
    yankNameSpaces(cur);
    ds = xmlSecDSigCtxCreate(NULL);
    assert(ds);
    ds->flags |= XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES | XMLSEC_DSIG_FLAGS_STORE_SIGNATURE;
    ds->signKey = loadRootKey();
    assert(!xmlSecDSigCtxSign(ds, signode));
    ds->userData = prop;
    return ds;
  nextconfig:
    tmp = configs;
    configs = configs->next;
    tmp->next = NULL;
    g_slist_free(tmp);
  }
  if(configs)
    g_slist_free(configs);
  return NULL;
}

